﻿using FreeSql.DataAnnotations;
using Singer.BackgroundJob.RabbitMQ.Contracts;
using Singer.BackgroundJob.RabbitMQ.Contracts.BackgroundJobLogs;
using Singer.Core;
using Singer.Shared.Domain;

namespace Singer.BackgroundJob.RabbitMQ.BackgroundJobLogs;

/// <summary>
/// 后台任务 日志实体
/// </summary>
public class BackgroundJobLog : IEntity<string>, ICreator, ISoftDelete
{
    /// <summary>
    /// Id
    /// </summary>
    [Column(IsPrimary = true)]
    public string Id { get; init; } = Cores.Guid;

    /// <summary>
    /// 任务参数类型名称（全名）
    /// </summary>
    public string ArgsType { get; init; }

    /// <summary>
    /// 任务参数 json字符串
    /// </summary>
    [Column(DbType = "nvarchar(1000)")]
    public string Args { get; init; }

    /// <summary>
    /// 任务执行器类型名称 （全名）
    /// </summary>
    public string ExecutorType { get; init; }

    /// <summary>
    /// 任务执行器标题
    /// </summary>
    public string Title { get; init; }

    /// <summary>
    /// 是否是系统级别任务
    /// </summary>
    public bool IsSystemJob { get; init; }

    /// <summary>
    /// 后台任务日志等级
    /// </summary>
    public BackgroundJobLogLevel LogLevel { get; init; } = BackgroundJobLogLevel.Middle;

    /// <summary>
    /// 任务状态
    /// </summary>
    public BackgroundJobLogStatus Status { get; protected set; } = BackgroundJobLogStatus.InQueue;

    /// <summary>
    /// 异常信息
    /// </summary>
    public string ErrorMessage { get; protected set; }

    /// <summary>
    /// 执行结果
    /// </summary>
    [Column(DbType = "nvarchar(2000)")]
    public string? Result { get; protected set; }

    /// <summary>
    /// 开始运行时间
    /// </summary>
    public DateTime? StartTime { get; protected set; }

    /// <summary>
    /// 结束运行时间
    /// </summary>
    public DateTime? EndTime { get; protected set; }

    /// <summary>
    /// 是否失败重试
    /// </summary>
    public bool FailRetry { get; init; }

    /// <summary>
    /// 最大重试次数
    /// </summary>
    public int MaxRetryCount { get; init; }

    /// <summary>
    /// 失败后，自动重试次数
    /// </summary>
    public int FailAutoRetryCount { get; protected set; }

    #region 基础字段
    public DateTime CreateTime { get; init; }
    public string CreatorId { get; protected set; }
    public string CreatorName { get; protected set; }
    public bool IsDeleted { get; protected set; }
    public DateTime? DeleteTime { get; protected set; }
    public string DeleterId { get; protected set; }
    public string DeleterName { get; protected set; }
    #endregion

    /// <summary>
    /// 消息路由key
    /// </summary>
    public string RoutingKey => $"{Contants.ROUTINGKEY_PREFIX}{ArgsType.ToLower()}";

    /// <summary>
    /// 默认构造函数(ORM使用)
    /// </summary>
    protected BackgroundJobLog()
    {

    }

    internal BackgroundJobLog(ConsumerContext consumerContext, object args)
    {
        // 当Type为 匿名类型 或 动态类型时，FullName会为NULL，这种类型不支持，抛异常。
        ArgsType = consumerContext.ArgsType.FullName ?? throw new Exception("后台任务参数类型不支持");
        ExecutorType = consumerContext.ExecutorType.FullName ?? throw new Exception("后台任务执行器类型不支持");
        Title = consumerContext.ExecutorAttribute.Title;
        IsSystemJob = consumerContext.ExecutorAttribute.IsSystemJob;
        LogLevel = consumerContext.ExecutorAttribute.LogLevel;
        FailRetry = consumerContext.ExecutorAttribute.FailRetry;
        MaxRetryCount = consumerContext.ExecutorAttribute.MaxRetryCount;
        Args = JsonUtils.ToJson(args);
        Status = BackgroundJobLogStatus.InQueue;
    }

    /// <summary>
    /// 初始化
    /// </summary>
    internal void Init()
    {
        Status = BackgroundJobLogStatus.Init;
        StartTime = null;
        EndTime = null;
        Result = null;
    }

    /// <summary>
    /// 任务开始运行
    /// </summary>
    internal void Running()
    {
        Status = BackgroundJobLogStatus.Running;
        StartTime = Cores.BeiJingNow;
    }

    /// <summary>
    /// 任务手动重启中
    /// </summary>
    internal bool ReStarting()
    {
        return ChangeStatus(BackgroundJobLogStatus.Restarting);
    }

    /// <summary>
    /// 任务执行成功
    /// </summary>
    internal bool Success()
    {
        EndTime = Cores.BeiJingNow;
        return ChangeStatus(BackgroundJobLogStatus.Success);
    }

    /// <summary>
    /// 保存任务执行结果
    /// </summary>
    internal void SaveResult<T>(T result) where T : notnull
    {
        Result = JsonUtils.ToJson(result);
    }

    /// <summary>
    /// 任务执行失败
    /// </summary>
    /// <param name="errorMessage">异常信息</param>
    /// <returns>是否重试</returns>
    internal bool Fail(string? errorMessage = null)
    {
        ChangeStatus(BackgroundJobLogStatus.Failed);
        ErrorMessage = errorMessage ?? "任务执行失败";
        EndTime = Cores.BeiJingNow;
        bool retry = FailRetry && FailAutoRetryCount < MaxRetryCount;
        if (retry)
        {
            Status = BackgroundJobLogStatus.Restarting;
            FailAutoRetryCount++;
        }
        return retry;
    }

    /// <summary>
    /// 开始取消任务
    /// </summary>
    internal bool Cancelling(string? message = null)
    {
        return ChangeStatus(BackgroundJobLogStatus.Cancelling);
    }

    /// <summary>
    /// 取消任务
    /// </summary>
    internal bool Canceled()
    {
        EndTime = Cores.BeiJingNow;
        return ChangeStatus(BackgroundJobLogStatus.Canceled);
    }

    /// <summary>
    /// 改变任务状态
    /// </summary>
    /// <param name="newStatus">新状态</param>
    protected bool ChangeStatus(BackgroundJobLogStatus newStatus, string? statusMessage = null)
    {
        // 允许变更的状态
        List<BackgroundJobLogStatus> allowStauses = Status switch
        {
            BackgroundJobLogStatus.InQueue => [BackgroundJobLogStatus.Init, BackgroundJobLogStatus.Cancelling],
            BackgroundJobLogStatus.Init => [BackgroundJobLogStatus.Running, BackgroundJobLogStatus.Cancelling],
            BackgroundJobLogStatus.Running => [BackgroundJobLogStatus.Success, BackgroundJobLogStatus.Cancelling],
            BackgroundJobLogStatus.Cancelling => [BackgroundJobLogStatus.Canceled],
            BackgroundJobLogStatus.Canceled => [BackgroundJobLogStatus.Restarting],
            BackgroundJobLogStatus.Restarting => [BackgroundJobLogStatus.Running, BackgroundJobLogStatus.Cancelling],
            BackgroundJobLogStatus.Failed => [BackgroundJobLogStatus.Restarting],
            _ => [],
        };
        allowStauses.Add(BackgroundJobLogStatus.Failed);
        if (!allowStauses.Contains(newStatus))
            return false;
        Status = newStatus;
        return true;
    }
}
